import os
import pathlib
import re
import shutil
import markdown
import json
from docxcompose.composer import Composer
from docx import Document
from docx.dml.color import ColorFormat
from docx.shared import RGBColor

DIRECTORY_WITH_MARKDOWN_FILES = os.environ.get(
    "DIRECTORY_WITH_MARKDOWN_FILES", "./docs"
)
OUTPUT_DIRECTORY = os.environ.get("OUTPUT_DIRECTORY", "./output")
HIERARCHY_OF_DOCS = {}
BATCH_SIZE = 10
TITLE_DOCX = os.environ.get("TITLE_DOCX", "./assets/title.docx")
COMBINED_DOCX_NAME = os.environ.get("COMBINED_DOCX_NAME", "combined.docx")
BEGIN_LABEL_DOCUMENT = os.environ.get("BEGIN_LABEL_DOCUMENT", "Руководство")


def delete_directory(directory_path):
    if os.path.exists(directory_path):
        shutil.rmtree(directory_path)
        print(f"The {directory_path} has been deleted successfully")
    else:
        print(f"The {directory_path} does not exist!")


def get_output_docx_file_path(output_directory, file_path):
    file_directory = os.path.dirname(file_path)
    path_name, _ = os.path.splitext(file_path)
    file_name = path_name.split("/")[-1]
    output_file_directory = os.path.join(
        output_directory, re.sub("^./", "", file_directory)
    )
    output_file_path = os.path.join(output_file_directory, f"{file_name}.docx")
    return output_file_path


def build_output_docx_file(file_path, output_file_path):
    file_directory = os.path.dirname(file_path)
    path_name, _ = os.path.splitext(file_path)
    file_name = path_name.split("/")[-1]
    builded_docx_path = os.path.join(file_directory, f"{file_name}.docx")

    # https://stackoverflow.com/questions/11338049/how-to-convert-html-with-mathjax-into-latex-using-pandoc
    # https://stackoverflow.com/questions/31955390/pandoc-markdown-to-docx-with-latex-equations-as-embedded-images
    command = f"cd {file_directory} && pandoc {file_name}.md --webtex -t html | pandoc -f html -o {file_name}.docx"
    os.system(command)

    output_file_directory = os.path.dirname(output_file_path)
    if not os.path.exists(output_file_directory):
        os.makedirs(output_file_directory)

    shutil.copy(builded_docx_path, output_file_path)
    os.remove(builded_docx_path)


def add_dirs_to_hierarchy(dirs_levels, root, dirs):
    dirs_levels[root] = []
    dirs_in_root = []
    for dir in dirs:
        dir_path = os.path.join(root, dir)
        category_json_path = os.path.join(dir_path, "_category_.json")
        lvl = 0
        label = ""
        if os.path.exists(category_json_path):
            with open(category_json_path) as f:
                d = json.load(f)
                if "position" in d:
                    lvl = d["position"]
                if "label" in d:
                    label = d["label"]

        dirs_in_root.append(
            {"level": lvl, "directory": dir, "full_path": dir_path, "label": label}
        )
        dirs_in_root.sort(key=lambda x: x["level"])
        dirs_levels[root] = dirs_in_root


def add_file_to_hierarchy(dirs_levels, file, file_path):
    file_directory = os.path.dirname(file_path)

    data = pathlib.Path(file_path).read_text(encoding="utf-8")
    md = markdown.Markdown(extensions=["meta"])
    md.convert(data)
    print("Markdown meta:", md.Meta)
    lvl = 0
    if "sidebar_position" in md.Meta:
        lvl = int(md.Meta["sidebar_position"][0])
    dirs_levels[file_directory].append(
        {"level": lvl, "file": file, "full_path": file_path}
    )
    dirs_levels[file_directory].sort(key=lambda x: x["level"])


def get_output_files(dirs_levels, current_dir, output_directory, directory_label):
    files_path = []
    if not current_dir in dirs_levels:
        return files_path

    for child in dirs_levels[current_dir]:
        if "file" in child:
            file_to_add = {
                "full_path": get_output_docx_file_path(
                    output_directory, child["full_path"]
                ),
                "directory_label": directory_label,
            }
            files_path.append(file_to_add)
        if "directory" in child:
            child_files = get_output_files(
                dirs_levels,
                child["full_path"],
                output_directory,
                ">".join([directory_label, child["label"]]),
            )
            files_path += child_files
    return files_path


# https://stackoverflow.com/questions/8290397/how-to-split-an-iterable-in-constant-size-chunks
def batch(iterable, n=1):
    l = len(iterable)
    for ndx in range(0, l, n):
        yield iterable[ndx : min(ndx + n, l)]


if __name__ == "__main__":
    delete_directory(OUTPUT_DIRECTORY)

    for root, dirs, files in os.walk(DIRECTORY_WITH_MARKDOWN_FILES):
        add_dirs_to_hierarchy(HIERARCHY_OF_DOCS, root, dirs)

        for file in files:
            if file.endswith(".md"):
                output_file = os.path.join(root, file)
                print(f"> Process file {output_file}")
                add_file_to_hierarchy(HIERARCHY_OF_DOCS, file, output_file)

                output_file_path = get_output_docx_file_path(
                    OUTPUT_DIRECTORY, output_file
                )
                print(f"> Build docx {output_file_path}")
                build_output_docx_file(output_file, output_file_path)

    ordered_output_files = get_output_files(
        HIERARCHY_OF_DOCS,
        DIRECTORY_WITH_MARKDOWN_FILES,
        OUTPUT_DIRECTORY,
        BEGIN_LABEL_DOCUMENT,
    )

    temp_docx_file = os.path.join(OUTPUT_DIRECTORY, COMBINED_DOCX_NAME)

    # https://stackoverflow.com/questions/69331418/how-can-i-merge-a-document-containing-hyperlinks-docx-to-an-other-docx-using-p
    master = Document(TITLE_DOCX)
    master.add_page_break()
    composer = Composer(master)
    count_of_document = len(ordered_output_files)
    index = 0
    for output_file in ordered_output_files:
        output_file_path = output_file["full_path"]
        print(f"> Start join file {output_file_path}")
        path_doc = Document()
        p = path_doc.add_paragraph()
        run = p.add_run(output_file["directory_label"])
        run.bold = True
        run.font.color.rgb = RGBColor(0x42, 0x24, 0xE9)

        composer.append(path_doc)
        sub_doc = Document(output_file_path)
        # https://stackoverflow.com/questions/24872527/combine-word-document-using-python-docx
        if index < count_of_document - 1:
            sub_doc.add_page_break()
        composer.append(sub_doc)
        index += 1
    composer.save(temp_docx_file)

    print(
        f"Combined docx {temp_docx_file} file was saved."

    )

    print(
        f"Everything is successfully completed! Documents processed: {len(ordered_output_files)}."
    )
